home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Converters / Convert_FONT / Source / shared.subproj / RCS / MacToNeXTText.m,v < prev    next >
Text File  |  1995-06-12  |  18KB  |  505 lines

  1. head     1.2;
  2. branch   ;
  3. access   ;
  4. symbols  beta10:1.1;
  5. locks    death:1.2;
  6. comment  @@;
  7.  
  8.  
  9. 1.2
  10. date     93.04.04.23.44.36;  author death;  state Exp;
  11. branches ;
  12. next     1.1;
  13.  
  14. 1.1
  15. date     93.01.10.15.08.03;  author death;  state Exp;
  16. branches ;
  17. next     ;
  18.  
  19.  
  20. desc
  21. @@
  22.  
  23.  
  24. 1.2
  25. log
  26. @Sun Apr  4 23:44:36 PDT 1993
  27. @
  28. text
  29. @/*====================================================================
  30. This is the implementation file for the MacToNeXTText class.  Full documentation for this class can be found in the MacToNeXTText.rtf file.  I will not duplicate all that fine information here.
  31.  
  32. NOTE: You may find that text doesn't line up properly unless you use the New Century Schoolbook Roman typeface, since this was created with it.
  33.  
  34. INFORMATION:
  35.     This is $Revision: 1.1 $ of this file
  36.     It was last modified by $Author: death $ on $Date: 93/01/10 15:08:03 $
  37.      $Log:    MacToNeXTText.m,v $
  38. Revision 1.1  93/01/10  15:08:03  death
  39. Sun Jan 10 15:08:03 PST 1993
  40.  
  41. ====================================================================*/
  42.  
  43.  
  44. #import "MacToNeXTText.h"
  45. #import <memory.h>    // for memcpy
  46. #include <strings.h>
  47.  
  48.  
  49. @@implementation MacToNeXTText
  50.  
  51.  
  52. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  53. //    Routine:        init:
  54. //    Parameters:    none
  55. //    Returns:        self
  56. //    Stores:        none
  57. //    Description:
  58. //        This initalizes the object by filling in the convert array that it uses to
  59. //        dictate most of its character conversions.
  60. //    Bugs:
  61. //        Note that we store a null to indicate any characters that can't be
  62. //        converted directly.  Because of this strategy, the null character must be dealt
  63. //        with in this object, though really it is part of the superclass' territory.
  64. //        other more complex implementations (keep a flag with each array entry
  65. //        to indicate if we can convert to it, or keep integers so one can store more than
  66. //        one of 256 values in an entry  might be used to reimplement this if needed.
  67. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  68. - (Instance) init
  69. {
  70.     Integer    counter;
  71.     //
  72.     [super init];
  73.  
  74.     StrictIM = NO;
  75.     //
  76.     //    Initialize conversion array with whatever our superclass likes to do.
  77.     //
  78.     for (counter = 0; counter < 256; counter++)
  79.         ConvertArray[counter] = [super    ConvertCharacter: (Character) counter];
  80.     //
  81.     //    We view the world as: we are just like whatever our superclass is, except
  82.     //    that we differ for characters 0,  CR, and anything above 128. =)  So,
  83.     //    modify the table to reflect all of this.  (note: indices are essencially mac
  84.     //    character codes, while values we are assigning are NeXT character codes).
  85.     //
  86.     ConvertArray[CARRIAGERETURN] = NEWLINE; // Convert mac CR's to LF's
  87.     ConvertArray[0x11] = NullCharacter;    // (commandsymbol)
  88.     ConvertArray[0x12] = NullCharacter;    // (check)
  89.     ConvertArray[0x13] = NullCharacter;    // (diamond)
  90.     ConvertArray[0x14] = NullCharacter;    // (apple)
  91.  
  92.     ConvertArray[0x80] = 0x85;            // (Adieresis)
  93.     ConvertArray[0x81] = 0x86;            // (Aring)
  94.     ConvertArray[0x82] = 0x87;            // (Ccedilla)
  95.     ConvertArray[0x83] = 0x89;            // (Eacute)
  96.     ConvertArray[0x84] = 0x91;            // (Ntilde)
  97.     ConvertArray[0x85] = 0x96;            // (Odieresis)
  98.     ConvertArray[0x86] = 0x9A;            // (Udieresis)
  99.     ConvertArray[0x87] = 0xD6;            // (aacute)
  100.     ConvertArray[0x88] = 0xD5;            // (agrave)
  101.     ConvertArray[0x89] = 0xD7;            // (acircumflex)
  102.     ConvertArray[0x8A] = 0xD9;            // (adieresis)
  103.     ConvertArray[0x8B] = 0xD8;            // (atilde)
  104.     ConvertArray[0x8C] = 0xDA;            // (aring)
  105.     ConvertArray[0x8D] = 0xDB;            // (ccedilla)
  106.     ConvertArray[0x8E] = 0xDD;            // (eacute)
  107.     ConvertArray[0x8F] = 0xDC;            // (egrave)
  108.     //    0x9X
  109.     ConvertArray[0x90] = 0xDE;            // (ecircumflex)
  110.     ConvertArray[0x91] = 0xDF;            // (dieresis)
  111.     ConvertArray[0x92] = 0xE2;            // (iacute)
  112.     ConvertArray[0x93] = 0xE0;            // (igrave)
  113.     ConvertArray[0x94] = 0xE4;            // (icircumflex)
  114.     ConvertArray[0x95] = 0xE5;            // (idieresis)
  115.     ConvertArray[0x96] = 0xE7;            // (ntilde)
  116.     ConvertArray[0x97] = 0xED;            // (oacute)
  117.     ConvertArray[0x98] = 0xEC;            // (ograve)
  118.     ConvertArray[0x99] = 0xEE;            // (ocircumflex)
  119.     ConvertArray[0x9A] = 0xF0;            // (odieresis)
  120.     ConvertArray[0x9B] = 0xEF;            // (otilde)
  121.     ConvertArray[0x9C] = 0xF3;            // (uacute)
  122.     ConvertArray[0x9D] = 0xF2;            // (ugrave)
  123.     ConvertArray[0x9E] = 0xF4;            // (ucircumflex)
  124.     ConvertArray[0x9F] = 0xF6;            // (udieresis)
  125.     //    0xAX
  126.     ConvertArray[0xA0] = 0xB2;            // (dagger)
  127.     ConvertArray[0xA1] = NullCharacter;    // (degree)
  128.     ConvertArray[0xA2] = 0xA2;            // (cent)
  129.     ConvertArray[0xA3] = 0xA3;            // (sterling)
  130.     ConvertArray[0xA4] = 0xA7;            // (section)
  131.     ConvertArray[0xA5] = 0xB7;            // (bullet)
  132.     ConvertArray[0xA6] = 0xB6;            // (paragraph)
  133.     ConvertArray[0xA7] = 0xFB;            // (germandbls)
  134.     ConvertArray[0xA8] = 0xB0;            // (registerserif)
  135.     ConvertArray[0xA9] = 0xA0;            // (copyrightserif)
  136.     ConvertArray[0xAA] = NullCharacter;    // (trademarkserif)
  137.     ConvertArray[0xAB] = 0xC2;            // (acute)
  138.     ConvertArray[0xAC] = 0xC8;            // (dieresis)
  139.     ConvertArray[0xAD] = NullCharacter;    // (notequal)
  140.     ConvertArray[0xAE] = 0xE1;            // (AE)
  141.     ConvertArray[0xAF] = 0xE9;            // (Oslash)
  142.     //    0xBX
  143.     ConvertArray[0xB0] = NullCharacter;    //(infinity)
  144.     ConvertArray[0xB1] = 0xD1;            // (plusminus)
  145.     ConvertArray[0xB2] = NullCharacter;    // (lessequal)
  146.     ConvertArray[0xB3] = NullCharacter;    // (greaterequal)
  147.     ConvertArray[0xB4] = 0xA5;            // (yen)
  148.     ConvertArray[0xB5] = 0x9D;            // (mu)
  149.     ConvertArray[0xB6] = NullCharacter;    // (partialdiff)
  150.     ConvertArray[0xB7] = NullCharacter;    // (summation)
  151.     ConvertArray[0xB8] = NullCharacter;    // (product) 
  152.     ConvertArray[0xB9] = NullCharacter;    // (pi)
  153.     ConvertArray[0xBA] = NullCharacter;    // (integral)
  154.     ConvertArray[0xBB] = 0xE3;            // (ordfeminine)
  155.     ConvertArray[0xBC] = 0xEB;            // (ordmasculine)
  156.     ConvertArray[0xBD] = NullCharacter;    // (Omega)
  157.     ConvertArray[0xBE] = 0xF1;            // (ae)
  158.     ConvertArray[0xBF] = 0xF9;            // (oslash)
  159.     //    0xC0
  160.     ConvertArray[0xC0] = 0xBF;            // (questiondown)
  161.     ConvertArray[0xC1] = 0xA1;            // (exclamdown)
  162.     ConvertArray[0xC2] = 0xBE;            // (logicalnot)
  163.     ConvertArray[0xC3] = NullCharacter;    // (radical)
  164.     ConvertArray[0xC4] = 0xA6;            // (florin)
  165.     ConvertArray[0xC5] = NullCharacter;    // (approxequal)
  166.     ConvertArray[0xC6] = NullCharacter;    // (delta)
  167.     ConvertArray[0xC7] = 0xAB;            // (guillemotleft)
  168.     ConvertArray[0xC8] = 0xBB;            // (guillemotright)
  169.     ConvertArray[0xC9] = 0xBC;            // (elipsis)
  170.     ConvertArray[0xCA] = 0x80;            // (nbspace)   next calls it: (figsp)
  171.     ConvertArray[0xCB] = 0x81;            // (Agrave)
  172.     ConvertArray[0xCC] = 0x84;            // (Atilde)
  173.     ConvertArray[0xCD] = 0x95;            // (Otilde)
  174.     ConvertArray[0xCE] = 0xEA;            // (OE)
  175.     ConvertArray[0xCF] = 0xFA;            // (oe)
  176.     //    0xD0
  177.     ConvertArray[0xD0] = 0xB1;            // (endash)
  178.     ConvertArray[0xD1] = 0xD0;            // (emdash)
  179.     ConvertArray[0xD2] = 0xAA;            // (quotedblleft)
  180.     ConvertArray[0xD3] = 0xBA;            // (quotedblright)
  181.     ConvertArray[0xD4] = 0x60;            // (quoteleft)
  182.     ConvertArray[0xD5] = 0x27;            // (quoteright)
  183.     ConvertArray[0xD6] = 0x9F;            // (divide)
  184.     ConvertArray[0xD7] = NullCharacter;    // (lozenge)
  185.     ConvertArray[0xD8] = 0xFD;            // (ydieresis)
  186.     ConvertArray[0xD9] = NullCharacter;    // (Ydieresis)  (usually a picture in IM fonts)
  187.     ConvertArray[0xDA] = 0xA4;            // (fraction)
  188.     ConvertArray[0xDB] = 0xA8;            // (currency)
  189.     ConvertArray[0xDC] = 0xAC;            // (guilsinglleft)
  190.     ConvertArray[0xDD] = 0xAD;            // (guilsinglright)
  191.     ConvertArray[0xDE] = 0xAE;            // (fi)
  192.     ConvertArray[0xDF] = 0xAF;            // (fl)
  193.     //    0xE0
  194.     ConvertArray[0xE0] = 0xB3;            // (daggerdbl)
  195.     ConvertArray[0xE1] = 0xB4;            // (periodcentered)
  196.     ConvertArray[0xE2] = 0xB8;            // (quotesinglebase)
  197.     ConvertArray[0xE3] = 0xB9;            // (quotedblbase)
  198.     ConvertArray[0xE4] = 0xBD;            // (perthousand)
  199.     ConvertArray[0xE5] = 0x83;            // (Acircumflex)
  200.     ConvertArray[0xE6] = 0x8A;            // (Ecircumflex)
  201.     ConvertArray[0xE7] = 0x82;            // (Aacute)
  202.     ConvertArray[0xE8] = 0x8B;            // (Edieresis)
  203.     ConvertArray[0xE9] = 0x88;            // (Egrave)
  204.     ConvertArray[0xEA] = 0x8D;            // (Iacute)
  205.     ConvertArray[0xEB] = 0x8E;            // (Icircumflex)
  206.     ConvertArray[0xEC] = 0x8F;            // (Idieresis)
  207.     ConvertArray[0xED] = 0x8C;            // (Igrave)
  208.     ConvertArray[0xEE] = 0x93;            // (Oacute)
  209.     ConvertArray[0xEF] = 0x94;            // (Ocircumflex)
  210.     //    0xF0
  211.     ConvertArray[0xF0] = NullCharacter;    // (apple)
  212.     ConvertArray[0xF1] = 0x92;            // (Ograve)
  213.     ConvertArray[0xF2] = 0x98;            // (Uacute)
  214.     ConvertArray[0xF3] = 0x99;            // (Ucircumflex)
  215.     ConvertArray[0xF4] = 0x97;            // (Ugrave)
  216.     ConvertArray[0xF5] = 0xF5;            // (dotlessi)
  217.     ConvertArray[0xF6] = 0xC3;            // (circumflex)
  218.     ConvertArray[0xF7] = 0xC4;            // (tilde)
  219.     ConvertArray[0xF8] = 0xC5;            // (macron)
  220.     ConvertArray[0xF9] = 0xC6;            // (breve)
  221.     ConvertArray[0xFA] = 0xC7;            // (dotaccent)
  222.     ConvertArray[0xFB] = 0xCA;            // (ring)
  223.     ConvertArray[0xFC] = 0xCB;            // (cedilla)
  224.     ConvertArray[0xFD] = 0xCD;            // (hungarumlaut)
  225.     ConvertArray[0xFE] = 0xCE;            // (ogonek)
  226.     ConvertArray[0xFF] = 0xCF;            // (caron)
  227.     
  228.     return self;
  229. }
  230.  
  231.  
  232.  
  233.  
  234. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  235. //    Routine:        ConvertCharacter:
  236. //    Parameters:    a character to be converted
  237. //    Returns:        the converted character
  238. //    Stores:        the character that we are returning.
  239. //    Description:
  240. //        This uses the ConvertArray set up in the initialization to convert
  241. //        a character from a Mac character set to a NeXT.  It allows for strict
  242. //        adherance to the table in Inside Mac vol 1. p. 221.  It returns the converted
  243. //        character and a result code based on its succes.
  244. //    Bugs:
  245. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  246. - (Character) ConvertCharacter: (Character) theCharacter
  247. {
  248.     Character    result;
  249.     Boolean        couldconvert = YES;
  250.     [self ResetResults];
  251.     //
  252.     //    Determine if we are doing 'strict inside mac vol 1 p. 221' conversions.
  253.     //    if so, and the character is undefind in IM, return character literally.
  254.     //
  255.     if ((theCharacter > 0xD8) && (StrictIM == YES))
  256.         result = theCharacter;
  257.     else
  258.     {
  259.         //
  260.         //    In general, look up our result in the conversion array.  if we get a
  261.         //    null back (and we don't didn't pass a null), indicate we could not
  262.         //    convet properly.
  263.         //
  264.         result = ConvertArray[theCharacter];
  265.         if ((result == NullCharacter) &&  (theCharacter != NullCharacter))
  266.         {
  267.             result = theCharacter;
  268.             couldconvert = NO;
  269.         }
  270.     }
  271.     //
  272.     //    Store the result, and return.
  273.     //
  274.     [self    PutCharacter: result Into: FIRST_RESULT];
  275.     if (couldconvert == YES)
  276.         [self    StoreErrorCode: errOK AndText: "Character converted!"];
  277.     else
  278.         [self    StoreErrorCode: errCANTMAPTOONE AndText: "No equivalent standard NeXTSTEP character"];
  279.     return result;
  280. }
  281.  
  282.  
  283. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  284. //    Routine:        ConvertString:WithLength:
  285. //    Parameters:    a pointer to a string of data to be converted
  286. //                the length of the data the poiner poins to.
  287. //    Returns:        a poiner to a new block of data to be converted
  288. //    Stores:        the pointer we are returning
  289. //                the length of the new data
  290. //    Description:
  291. //        This converts the text in the source data area into a new area.
  292. //        This is passed a string of characters.  This then creates a new string,
  293. //        and converts all the source characters from a Macintosh encoding to
  294. //        the destination string.  This differs from ConvertCharacter:, above, int only
  295. //        two ways:  The first is that this processes multiple characters at once.
  296. //        the second is that if it finds a character that's doens't map to a specific
  297. //        alternate character, it will replace it with a multi character string.  E.g.
  298. //        less  than or equal to is replaced with <=  And the apple symbol is just
  299. //        replaced with [apple].  The source string is not altered in any way.  The
  300. //        caller is responsible for disposing of the returned string.
  301. //    Bugs:
  302. //        we don't really check for errors anywhere (not taht there are many oportunities)...
  303. //    History
  304. //        92.12.31    djb    Added a null-character termination to the string.
  305. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  306. - (Pointer) ConvertString: (Pointer) theData WithLength: (Integer) length
  307. {
  308.     Character*    source = (Character*) theData; // getting a better typed ptr.
  309.     Integer        index,  destindex = 0;
  310.     Integer        destsize = length*1.5;
  311.     Integer        replacelength;
  312.     CString        replacement;
  313.     Character*    temp, *dest = (Character*) NewPointer(destsize);
  314.     Character    result;
  315.     [self    ResetResults];
  316.     //
  317.     for (index = 0; index < length; index++)
  318.     {
  319.         //
  320.         //    Determine if we are doing 'strict inside mac vol 1 p. 221' conversions.
  321.         //    if so, and the character is undefind in IM, return character literally.
  322.         //    Otherwise, just get whatever the standard conversion is.
  323.         //
  324.         if ((source[index] > 0xD8)&&(StrictIM == YES))
  325.             result = source[index];
  326.         else
  327.             result = ConvertArray[source[index]];
  328.         //
  329.         //    If our result is not a null (or if we passed a null), stick the result into
  330.         //    the destination space.  increment the space if necessary.
  331.         //
  332.         if ((result != NullCharacter) ||  (source[index] == NullCharacter))
  333.         {
  334.             if (destindex >= destsize)
  335.             {
  336.                 temp = NewPointer(destsize +512);
  337.                 memcpy(temp, dest, destsize);
  338.                 FreePointer(dest);
  339.                 dest = temp;
  340.                 destsize += 512;
  341.             }
  342.             dest[destindex] = result;
  343.             destindex++;
  344.         }
  345.         else
  346.         {
  347.             //
  348.             //    We evidently got a null back, telling us 'yo! I can't convert this to
  349.             //    a different single character'.  So, now we determine which it was, and
  350.             //    determine what string to use in its place.  Generally, we use the PS
  351.             //    name for the character in []'s.
  352.             //
  353.             switch (source[index])
  354.             {
  355.                 case 0x11:            // (commandsymbol)
  356.                     replacement = "[commandsymbol]";
  357.                     break;
  358.                 case 0x12:            // (check)
  359.                     replacement = "[check]";
  360.                     break;
  361.                 case 0x13:            // (diamond)
  362.                     replacement = "[diamond]";
  363.                     break;
  364.                 case 0x14:            // (apple)
  365.                     replacement = "[apple]";
  366.                     break;
  367.                 case 0xA1:            // (degree)
  368.                     replacement = "[degrees]";
  369.                     break;
  370.                 case 0xAA:            // (trademarkserif)
  371.                     replacement = "[trademarkserif]";
  372.                     break;
  373.                 case 0xAD:            // (notequal)
  374.                     replacement = "<>";
  375.                     break;
  376.                 case 0xB0:            // (infinity)
  377.                     replacement = "[infinity]";
  378.                     break;
  379.                 case 0xB2:            // (lessequal)
  380.                     replacement = "<=";
  381.                     break;
  382.                 case 0xB3:            // (greaterequal)
  383.                     replacement = ">=";
  384.                     break;
  385.                 case 0xB6:            // (partialdiff)
  386.                     replacement = "[partialdiff]";
  387.                     break;
  388.                 case 0xB7:            // (summation)
  389.                     replacement = "[summation]";
  390.                     break;
  391.                 case 0xB8:            // (product) 
  392.                     replacement = "[product]";
  393.                     break;
  394.                 case 0xB9:            // (pi)
  395.                     replacement = "[pi]";
  396.                     break;
  397.                 case 0xBA:            // (integral)
  398.                     replacement = "[integral]";
  399.                     break;
  400.                 case 0xBD:            // (Omega)
  401.                     replacement = "[Omega]";
  402.                     break;
  403.                 case 0xC3:            // (radical)
  404.                     replacement = "[radical]";
  405.                     break;
  406.                 case 0xC5:            // (approxequal)
  407.                     replacement = "[approxequal]";
  408.                     break;
  409.                 case 0xC6:            // (delta)
  410.                     replacement = "[delta]";
  411.                     break;
  412.                 case 0xD7:            // (lozenge)
  413.                     replacement = "[lozenge]";
  414.                     break;
  415.                 case 0xD9:            // (Ydieresis)  (usually a picture in IM fonts)
  416.                     if (StrictIM == NO)
  417.                         replacement = "[Ydieresis(or picture)]";
  418.                     else
  419.                         replacement = "\331"; // the character itself.
  420.                     break;
  421.                 case 0xF0:            // (apple)
  422.                     if (StrictIM == NO)
  423.                         replacement = "[apple]";
  424.                     else
  425.                         replacement = "\360"; // the character itself.
  426.                     break;
  427.                 default:
  428.                     replacement = "[[THERE IS A BUG IN THE PROIGRAM (no joke)]]";
  429.                     break;
  430.             }
  431.             //
  432.             //    With a string in hand to use now, get it's length, and assure that we
  433.             //    have space to store it (if not, increment the memory block's size).
  434.             //    copy the string into the output memory space, and continue.
  435.             //
  436.             replacelength = strlen(replacement);
  437.             if ( (destindex+replacelength) >= destsize)
  438.             {
  439.                 temp = NewPointer(destsize + 512);
  440.                 memcpy(temp, dest, destsize);
  441.                 FreePointer(dest);
  442.                 dest = temp;
  443.                 destsize = destsize + 512;
  444.             }
  445.             memcpy(&dest[destindex], replacement, strlen(replacement));
  446.             destindex += replacelength;
  447.         }
  448.     }
  449.     //
  450.     //    Added, so that the block of memory returned is also null terminated.
  451.     //
  452.     if (destindex >= destsize)
  453.     {
  454.         temp = NewPointer(destsize +512);
  455.         memcpy(temp, dest, destsize);
  456.         FreePointer(dest);
  457.         dest = temp;
  458.         destsize += 512;
  459.     }
  460.     dest[destindex] = EndOfCString;
  461.  
  462.  
  463.     //
  464.     //    Store the result, and return.  (note that destindex always ends up pointing to
  465.     //    the next byte to be used, and thus is also a count of the total number of bytes
  466.     //    in the dest string.
  467.     //
  468.     [self    StorePointer: dest];
  469.     [self    PutPositiveInteger: destindex  Into: SECOND_RESULT];
  470.     [self    StoreErrorCode: errOK AndText: "Nothing could go wrong (pathetic program)!"];
  471.     return dest;
  472. }
  473.  
  474.  
  475. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  476. //    Routine:        UseIMVI:
  477. //    Parameters:    a boolean value
  478. //    Returns:        self
  479. //    Stores:        none
  480. //    Description:
  481. //        This simply allows the user to toggle whether they want to do strict IM
  482. //        conversions.
  483. //    Bugs:
  484. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  485. - UseIM1:  (Boolean) doItStrictly
  486. {
  487.     StrictIM = doItStrictly;
  488.     return self;
  489. }
  490. @@end
  491. @
  492.  
  493.  
  494. 1.1
  495. log
  496. @Sun Jan 10 15:08:03 PST 1993
  497. @
  498. text
  499. @d7 6
  500. a12 3
  501.     This is $Revision: 1.4 $ of this file
  502.     It was last modified by $Author: death $ on $Date: 92/04/05 22:51:36 $
  503.      $Log:    $
  504. @
  505.